﻿using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using Borg;

namespace Monitor
{
    class SlotButton : UserControl
    {
        private static readonly Color[] Colors =
            new[]
                {
                    Color.Orange,
                    Color.LightSteelBlue,
                    Color.YellowGreen,
                    Color.Yellow,
                    Color.Red,
                    Color.Silver
                };

        private static readonly Color[] Forecolors =
            new[]
                {
                    Color.DarkRed,
                    Color.SteelBlue,
                    Color.GreenYellow,
                    Color.SandyBrown,
                    Color.DarkRed,
                    Color.DarkSlateGray
                };

        private static int _cnt;
        private int _refCount = 1;
        private readonly IContainer components = null;
        private readonly ExtensionInfo _extension;
        private ColorBlend _blend;

        private Label _txtExtensionName;
        private Label _txtPluginName;
        private LinkLabel _releaseLink;
        private Label _txtSlots;
        private LinkLabel _shutDownLink;

        static SlotButton()
        { }

        private void ReleaseLinkLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            _extension.Release();
        }

        private static void ShutdownLinkLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Runtime.Shutdown();
        }

        private void InitializeComponent()
        {
            _txtExtensionName = new Label();
            _txtPluginName = new Label();
            _releaseLink = new LinkLabel();
            _txtSlots = new Label();
            _shutDownLink = new LinkLabel();
            SuspendLayout();
            _txtExtensionName.AutoSize = true;
            _txtExtensionName.Font = new Font("Tahoma", 10.2f, FontStyle.Bold, GraphicsUnit.Point, (byte)0);
            _txtExtensionName.Location = new Point(2, 7);
            _txtExtensionName.Margin = new Padding(2, 0, 2, 0);
            _txtExtensionName.Name = "_txtExtensionName";
            _txtExtensionName.Size = new Size(120, 17);
            _txtExtensionName.TabIndex = 2;
            _txtExtensionName.Text = "Extension.Name";
            _txtPluginName.AutoSize = true;
            _txtPluginName.Font = new Font("Tahoma", 9f, FontStyle.Regular, GraphicsUnit.Point, (byte)0);
            _txtPluginName.Location = new Point(3, 24);
            _txtPluginName.Margin = new Padding(2, 0, 2, 0);
            _txtPluginName.Name = "_txtPluginName";
            _txtPluginName.Size = new Size(74, 14);
            _txtPluginName.TabIndex = 3;
            _txtPluginName.Text = "Plugin.Name";
            _releaseLink.ActiveLinkColor = Color.Black;
            _releaseLink.AutoSize = true;
            _releaseLink.Font = new Font("Tahoma", 9f, FontStyle.Regular, GraphicsUnit.Point, (byte)0);
            _releaseLink.LinkBehavior = LinkBehavior.HoverUnderline;
            _releaseLink.Location = new Point(175, 25);
            _releaseLink.Margin = new Padding(2, 0, 2, 0);
            _releaseLink.Name = "_releaseLink";
            _releaseLink.Size = new Size(48, 14);
            _releaseLink.TabIndex = 4;
            _releaseLink.TabStop = true;
            _releaseLink.Text = "Release";
            _releaseLink.VisitedLinkColor = Color.Black;
            _releaseLink.LinkClicked += ReleaseLinkLinkClicked;
            _txtSlots.AutoEllipsis = true;
            _txtSlots.BackColor = SystemColors.ActiveCaption;
            _txtSlots.Font = new Font("Tahoma", 7.8f, FontStyle.Regular, GraphicsUnit.Point, 0);
            _txtSlots.ForeColor = SystemColors.ActiveCaptionText;
            _txtSlots.Location = new Point(133, 6);
            _txtSlots.Margin = new Padding(2, 0, 2, 0);
            _txtSlots.Name = "_txtSlots";
            _txtSlots.Size = new Size(92, 14);
            _txtSlots.TabIndex = 5;
            _txtSlots.Text = "Slot";
            _txtSlots.TextAlign = ContentAlignment.MiddleRight;
            _shutDownLink.ActiveLinkColor = Color.Black;
            _shutDownLink.AutoSize = true;
            _shutDownLink.Font = new Font("Tahoma", 9f, FontStyle.Regular, GraphicsUnit.Point, 0);
            _shutDownLink.LinkBehavior = LinkBehavior.HoverUnderline;
            _shutDownLink.Location = new Point(159, 25);
            _shutDownLink.Margin = new Padding(2, 0, 2, 0);
            _shutDownLink.Name = "_shutDownLink";
            _shutDownLink.Size = new Size(64, 14);
            _shutDownLink.TabIndex = 6;
            _shutDownLink.TabStop = true;
            _shutDownLink.Text = "Shutdown";
            _shutDownLink.Visible = false;
            _shutDownLink.VisitedLinkColor = Color.Black;
            _shutDownLink.LinkClicked += ShutdownLinkLinkClicked;
            AutoScaleDimensions = new SizeF(6f, 13f);
            AutoScaleMode = AutoScaleMode.Font;
            Controls.Add(_txtSlots);
            Controls.Add(_releaseLink);
            Controls.Add(_shutDownLink);
            Controls.Add(_txtPluginName);
            Controls.Add(_txtExtensionName);
            Margin = new Padding(2);
            Name = "SlotButton";
            Size = new Size(225, 49);
            ResumeLayout(false);
            PerformLayout();
        }

        public SlotButton()
        {
            InitializeComponent();

            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
        }

        private void SetOpenedSlots()
        {
            var builder = new StringBuilder();

            using (var slotEnumerator = _extension.SlotInfos.GetEnumerator())
            {
                while (slotEnumerator.MoveNext())
                {
                    var current = slotEnumerator.Current;

                    if (!current.IsOpen)
                        continue;

                    if (builder.Length > 0)
                        builder.Append(',');

                    builder.Append(current.Name);
                }
            }

            _txtSlots.Text = builder.ToString();
            _txtSlots.Visible = builder.Length > 0;
        }

        public static Color HSL2RGB(double hue, double saturation, double luminance)
        {
            double red;
            double green;
            double blue;

            if (luminance == 0.0)
            {
                double nul;
                blue = nul = 0.0;
                green = nul;
                red = nul;
            }

            else if (saturation == 0.0)
            {
                double nul;
                blue = nul = luminance;
                green = nul;
                red = nul;
            }
            else
            {
                var v = luminance <= 0.5
                    ? luminance * (1.0 + saturation)
                    : luminance + saturation - luminance * saturation;

                var l = 2.0 * luminance - v;

                var a =
                    new[]
                        {
                            hue + 1.0/3.0,
                            hue,
                            hue - 1.0/3.0
                        };

                var b = new double[3];

                for (var i = 0; i < 3; ++i)
                {
                    if (a[i] < 0.0)
                        ++a[i];

                    if (a[i] > 1.0)
                        --a[i];

                    b[i] =
                        6.0 * a[i] >= 1.0
                        ? (2.0 * a[i] >= 1.0
                            ? (3.0 * a[i] >= 2.0
                                ? l
                                : l + (v - l) * (2.0 / 3.0 - a[i]) * 6.0)
                            : v)
                        : l + (v - l) * a[i] * 6.0;
                }

                red = b[0];
                green = b[1];
                blue = b[2];
            }

            if (red > 1.0)
                red = 1.0;

            if (green > 1.0)
                green = 1.0;

            if (blue > 1.0)
                blue = 1.0;

            return Color.FromArgb((int)(byte.MaxValue * red), (int)(byte.MaxValue * green), (int)(byte.MaxValue * blue));
        }

        private Color GetColor(double luminance)
        {
            var backColor = BackColor;
            var hue = backColor.GetHue() / 360.0;
            backColor = BackColor;

            var saturation = backColor.GetSaturation() / 1.5;
            backColor = BackColor;

            var lum = backColor.GetBrightness() * 1.025;
            var color = HSL2RGB(hue, saturation, lum);

            var red = color.R * luminance;
            var green = color.G * luminance;
            var blue = color.B * luminance;

            if (red > byte.MaxValue)
                red = byte.MaxValue;

            if (green > byte.MaxValue)
                green = byte.MaxValue;
            
            if (blue > byte.MaxValue)
                blue = byte.MaxValue;
            
            return Color.FromArgb((int)red, (int)green, (int)blue);
        }

        private void InitBackground()
        {
            _blend =
                new ColorBlend
                    {
                        Colors =
                            new[]
                                {
                                    GetColor(1.075),
                                    GetColor(1.0),
                                    GetColor(0.925),
                                    GetColor(1.225)
                                },
                        Positions =
                            new[]
                                {
                                    0.0f,
                                    0.2f,
                                    0.2f,
                                    1f
                                }
                    };
        }

        public SlotButton(ExtensionInfo extension)
        {
            _extension = extension;
            InitializeComponent();

            var extensionTypeInfo = extension.ExtensionTypeInfo;

            _txtExtensionName.Text = extension.Name;
            _txtExtensionName.BackColor = Color.Transparent;

            _txtPluginName.Text = string.Format("{0}, Version {1}",
                extensionTypeInfo.PluginInfo.Name, extensionTypeInfo.PluginInfo.Version);

            _txtPluginName.BackColor = Color.Transparent;

            SetOpenedSlots();

            if (extension.Name != "Runtime")
            {
                _releaseLink.LinkColor = Forecolors[_cnt % Forecolors.Length];
                _releaseLink.BackColor = Color.Transparent;
            }
            else
            {
                _shutDownLink.LinkColor = Forecolors[_cnt % Forecolors.Length];
                _shutDownLink.BackColor = Color.Transparent;

                _releaseLink.Visible = false;
                _shutDownLink.Visible = true;
            }

            _txtSlots.BackColor = Forecolors[_cnt % Forecolors.Length];
            _txtSlots.ForeColor = Colors[_cnt % Colors.Length];

            BackColor = Colors[_cnt++ % Colors.Length];

            InitBackground();
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            e.Graphics.FillRectangle(
                new LinearGradientBrush(
                    DisplayRectangle,
                    Color.Transparent,
                    Color.Transparent,
                    LinearGradientMode.Vertical)
                    {InterpolationColors = _blend},
                e.ClipRectangle);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && components != null)
                components.Dispose();

            base.Dispose(disposing);
        }

        public void AddRef()
        {
            ++_refCount;
            SetOpenedSlots();
        }

        public bool Release()
        {
            if (--_refCount == 0)
                return true;
            
            SetOpenedSlots();
            return false;
        }
    }
}
